package springmvc.components.view;

import org.jsoup.Jsoup;
import org.jsoup.nodes.*;
import org.jsoup.select.Elements;
import org.jsoup.select.NodeTraversor;
import org.jsoup.select.NodeVisitor;
import server.request.HttpRequest;
import server.response.HttpResponse;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author cg
 * @date 2023/6/17 13:14
 */
public class View {
    private File viewFile;

    public View(File viewFile) {
        this.viewFile = viewFile;
    }

    /**
     * 渲染视图并返回给前端
     */
    public void render(HttpRequest request, HttpResponse response, Map<String, Object> model) throws IOException {
        //读取文件

        // 解析 HTML
        Document document = Jsoup.parse(viewFile, "utf-8");
        //进行for循环替换
        replaceEach(document, model);

        //进行标签占位符的替换
        replaceAttr(document, model);
        // 进行占位符替换操作...
        replacePlaceholders(document, model);

        // 获取替换后的 HTML
        String replacedHtml = document.outerHtml();

        // 输出替换后的 HTML
        response.writeAndFlush(replacedHtml);
    }

    private void replaceAttr(Document document, Map<String, Object> model) {
        Pattern pattern = Pattern.compile("@\\{([^}]+)}");
        Elements elements = document.select("*");
        for (Element element : elements) {
            for (Attribute attribute : element.attributes()) {
                if (attribute.getValue().contains("@{")) {
                    StringBuffer stringBuffer = replaceAll(model, pattern, attribute.getValue());
                    attribute.setValue(stringBuffer.toString());
                }
            }
        }
    }

    private void replaceEach(Document document, Map<String, Object> model) {
        // 选择要替换的所有总元素
        Elements allElements = document.select("[th:each]");
        //遍历要替换的总元素
        for (Element trElement : allElements) {
            // 获取th:each属性值
            String eachAttr = trElement.attr("th:each");
            // 解析属性值
            String[] values = eachAttr.split(":");
            String itemName = values[0].trim();
            String trim = values[1].trim();
            String itemsName = trim.substring(2, trim.length() - 1);
            // 生成新的内容
            List items = (List) model.get(itemsName); // 获取数据列表
            //存放新建的元素
            Stack<Element> newElements = new Stack<>();
            for (Object item : items) {
                //克隆一个相同的元素来替换
                Element textClone = trElement.clone();
                newElements.push(textClone);
                Elements elements = textClone.select("[th:text]");
                for (Element element : elements) {
                    //解析单个元素
                    String textAttr = element.attr("th:text");
                    textAttr = textAttr.substring(2, textAttr.length() - 1);
                    String[] textValue = textAttr.split("\\.");
                    if (textValue[0].trim().equals(itemName)) {
                        try {
                            Object o = null;
                            if (textValue.length == 1) {
                                o = item;
                            } else {
                                //获取对象中该属性名的值
                                Field declaredField = item.getClass().getDeclaredField(textValue[1].trim());
                                declaredField.setAccessible(true);
                                o = declaredField.get(item);
                            }
                            String text = element.text().replaceAll("\\$\\{}", o.toString());
                            element.text(text);
                            //将标签中的也替换
                            for (Attribute attribute : element.attributes()) {
                                String value = attribute.getValue();
                                value = value.replaceAll("\\$\\{}", o.toString());
                                attribute.setValue(value);
                            }
                            newElements.push(textClone);
                        } catch (NoSuchFieldException | IllegalAccessException e) {
                            continue;
                        }
                    }
                }
            }
            //填充新元素
            while (!newElements.isEmpty()) {
                trElement.after(newElements.pop());
            }
            // 删除原来的元素
            trElement.remove();
        }
    }

    /**
     * 替换单个位符
     *
     * @param document
     * @param model
     */
    private void replacePlaceholders(Document document, Map<String, Object> model) {
        Pattern pattern = Pattern.compile("\\$\\{([^}]+)}");
        Elements textNodes = document.body().select(":containsOwn(${)");

        for (Element textNode : textNodes) {
            String text = textNode.text();
            StringBuffer sb = replaceAll(model, pattern, text);

            textNode.text(sb.toString());
        }
    }

    private StringBuffer replaceAll(Map<String, Object> model, Pattern pattern, String text) {
        Matcher matcher = pattern.matcher(text);

        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String key = makeStringForRegExp(matcher.group(1)).replaceAll("\\\\", "");
            Object replacement = model.getOrDefault(key, ""); //找到替换的值
            if (replacement == null) {
                replacement = "";
            }
            matcher.appendReplacement(sb, replacement.toString());
        }
        if (!"".contentEquals(sb)) {
            matcher.appendTail(sb);
        }
        return sb;
    }

    //处理特殊字符
    public static String makeStringForRegExp(String str) {
        return str.replace("\\", "\\\\").replace("*", "\\*")
                .replace("+", "\\+").replace("|", "\\|")
                .replace("{", "\\{").replace("}", "\\}")
                .replace("(", "\\(").replace(")", "\\)")
                .replace("^", "\\^").replace("$", "\\$")
                .replace("[", "\\[").replace("]", "\\]")
                .replace("?", "\\?").replace(",", "\\,")
                .replace(".", "\\.").replace("&", "\\&");
    }
}
